#pragma once

#include "my_defs.h"
#include "my_spinlock.h"
#include "my_syscall.h"
#include "my_queue.h"
#include "my_thread_base.h"
#include "my_string.h"


#define MY_LOG_FLUSH_INTERVAL_MS            (5000)
#define MY_LOG_CHECK_ROLLING_INTERVAL_MS    (10000)
#define MY_LOG_BUF_SIZE                     (1 << 22)   // 4M
#define MY_LOG_MAX_PENDING_NUMS             (16)
#define MY_LOG_ITEM_SIZE                    (1 << 12)   // 4K


typedef enum {
    LOG_LEVEL_DEBUG = 1,
    LOG_LEVEL_WARN  = 2,
    LOG_LEVEL_ERROR = 3,
    LOG_LEVEL_FATAL = 4
} my_log_level_t;


typedef enum {
    LOG_ROLLING_MINUTE = 0,
    LOG_ROLLING_HOUR   = 1,
    LOG_ROLLING_DAY    = 2
} my_log_rolling_t;


typedef struct {
    char                                    *buf;
    size_t                                  used;
    my_queue_t                              queue;
    my_spin_wait_group_t                    wait_group;
} my_log_buf_t;


static inline my_log_buf_t *
my_log_buf_create()
{
    my_log_buf_t *log_buf;
    log_buf = my_thread_malloc(sizeof(my_log_buf_t));
    log_buf->used = 0;
    log_buf->buf = my_thread_malloc(MY_LOG_BUF_SIZE);
    my_spin_wait_group_init(&log_buf->wait_group);
    return log_buf;
}


static inline void
my_log_buf_free(my_log_buf_t *log_buf)
{
    if (log_buf == NULL) {
        return;
    }

    if (log_buf->buf != NULL) {
        my_thread_free(log_buf->buf);
    }

    my_thread_free(log_buf);
}


typedef struct {
    my_log_level_t                          level;
    // the log_path length must < 1024.
    const char                              *log_path;
    char                                    real_log_path[1280];
    int                                     fd;
    my_log_rolling_t                        rolling;

    my_spinlock_t                           lock;
    my_queue_t                              pending_bufs;
    int                                     pending_nums;
    my_log_buf_t                            *active_buf;
    int64_t                                 last_switch_ms;
    int64_t                                 last_check_rolling_ms;

    my_thread_t                             tid;
    my_mutex_t                              mutex;                             
    my_cond_t                               cond;

    my_atomic_bool_t                        stopped;
    my_atomic_int64_t                       lost;
    my_bool_t                               inited;
} my_log_t;

extern my_log_t                             def_log;


my_bool_t my_log_init(my_log_t *log, my_log_level_t level,
    const char *log_path, my_log_rolling_t rolling);


void my_log_output(my_log_t *log, const char *level_str,
    const char *file, int line, my_bool_t abort_proc,
    const char *fmt, ...);

void my_log_flush(my_log_t *log);

void my_log_stop(my_log_t *log);

#define MY_LOG_DEBUG(log, fmt, args...)                         \
    if (LOG_LEVEL_DEBUG >= (log)->level)                        \
        my_log_output(log, "debug", __FILE__, __LINE__, 0, fmt, ##args)

#define MY_LOG_WARN(log, fmt, args...)                          \
    if (LOG_LEVEL_WARN >= (log)->level)                         \
        my_log_output(log, "warn", __FILE__, __LINE__, 0, fmt, ##args)

#define MY_LOG_ERROR(log, fmt, args...)                         \
    if (LOG_LEVEL_ERROR >= (log)->level)                        \
        my_log_output(log, "error", __FILE__, __LINE__, 0, fmt, ##args)

#define MY_LOG_FATAL(log, fmt, args...)                         \
        my_log_output(log, "fatal", __FILE__, __LINE__, 1, fmt, ##args)


static inline void
my_def_log_init(my_log_level_t level,
    const char *log_file, my_log_rolling_t rolling)
{
    my_log_init(&def_log, level, log_file, rolling);
}

static inline void
my_def_log_flush()
{
    my_log_flush(&def_log);
}

static inline void
my_def_log_stop()
{
    my_log_stop(&def_log);
}



#define MY_DEF_LOG_DEBUG(fmt, args...)                          \
    if (LOG_LEVEL_DEBUG >= def_log.level)                       \
        my_log_output(&def_log, "debug", __FILE__, __LINE__, 0, fmt, ##args)

#define MY_DEF_LOG_WARN(fmt, args...)                           \
    if (LOG_LEVEL_WARN >= def_log.level)                        \
        my_log_output(&def_log, "warn", __FILE__, __LINE__, 0, fmt, ##args)

#define MY_DEF_LOG_ERROR(fmt, args...)                          \
    if (LOG_LEVEL_ERROR >= def_log.level)                       \
        my_log_output(&def_log, "error", __FILE__, __LINE__, 0, fmt, ##args)

#define MY_DEF_LOG_FATAL(fmt, args...)                          \
        my_log_output(&def_log, "fatal", __FILE__, __LINE__, 1, fmt, ##args)
